//
// ********************************************************************
// * License and Disclaimer                                           *
// *                                                                  *
// * The  Geant4 software  is  copyright of the Copyright Holders  of *
// * the Geant4 Collaboration.  It is provided  under  the terms  and *
// * conditions of the Geant4 Software License,  included in the file *
// * LICENSE and available at  http://cern.ch/geant4/license .  These *
// * include a list of copyright holders.                             *
// *                                                                  *
// * Neither the authors of this software system, nor their employing *
// * institutes,nor the agencies providing financial support for this *
// * work  make  any representation or  warranty, express or implied, *
// * regarding  this  software system or assume any liability for its *
// * use.  Please see the license in the file  LICENSE  and URL above *
// * for the full disclaimer and the limitation of liability.         *
// *                                                                  *
// * This  code  implementation is the result of  the  scientific and *
// * technical work of the GEANT4 collaboration.                      *
// * By using,  copying,  modifying or  distributing the software (or *
// * any work based  on the software)  you  agree  to acknowledge its *
// * use  in  resulting  scientific  publications,  and indicate your *
// * acceptance of all terms of the Geant4 Software license.          *
// ********************************************************************
//
//
// --------------------------------------------------------------
//   GEANT 4 - Underground Dark Matter Detector Advanced Example
//
//      For information related to this code contact: Alex Howard
//      e-mail: alexander.howard@cern.ch
// --------------------------------------------------------------
// Comments
//
//                  Underground Advanced
//               by A. Howard and H. Araujo 
//                    (27th November 2001)
//
// History/Additions:
// 16 Jan 2002  Added analysis
//
//
// EventAction program
// --------------------------------------------------------------

#include "DMXEventAction.hh"

// pass parameters for messengers:
#include "DMXRunAction.hh"
#include "DMXPrimaryGeneratorAction.hh"

// note DMXPmtHit.hh and DMXScintHit.hh are included in DMXEventAction.hh

#include "DMXEventActionMessenger.hh"

#ifdef G4ANALYSIS_USE
#include "DMXAnalysisManager.hh"
#endif

#include "G4Event.hh"
#include "G4EventManager.hh"
#include "G4HCofThisEvent.hh"
#include "G4VHitsCollection.hh"
#include "G4TrajectoryContainer.hh"
#include "G4Trajectory.hh"
#include "G4VVisManager.hh"
#include "G4SDManager.hh"
#include "G4UImanager.hh"
#include "G4UnitsTable.hh"
#include "G4ios.hh"
#include <fstream>
#include <iomanip>


//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
DMXEventAction::DMXEventAction(DMXRunAction* DMXRun,DMXPrimaryGeneratorAction* DMXGenerator) 
  : runAct(DMXRun),genAction(DMXGenerator)
{
  // create messenger
  eventMessenger = new DMXEventActionMessenger(this);

  // defaults for messenger
  drawColsFlag = "standard";
  drawTrksFlag = "all";
  drawHitsFlag = 1;
  savePmtFlag  = 0;
  saveHitsFlag = 1;

  printModulo = 1;

  // hits collections
  scintillatorCollID = -1;
  pmtCollID = -1;

  energy_pri=0;
  seeds=NULL;

}


//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
DMXEventAction::~DMXEventAction() {
  delete eventMessenger;
}


//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void DMXEventAction::BeginOfEventAction(const G4Event* evt) {

  // grab seeds
  seeds = genAction->GetEventSeeds();

  // grab energy of primary
  energy_pri = genAction->GetEnergyPrimary();

  event_id = evt->GetEventID();
  G4cout<<"EventID = "<<event_id<<G4endl;
 
  // get ID for scintillator hits collection
  if (scintillatorCollID==-1) {
    G4SDManager *SDman = G4SDManager::GetSDMpointer();
    scintillatorCollID = SDman->GetCollectionID("scintillatorCollection");
  }

  // get ID for pmt hits collection
  if (pmtCollID==-1) {
    G4SDManager *SDman = G4SDManager::GetSDMpointer();
    pmtCollID = SDman->GetCollectionID("pmtCollection");
  }

  for (int i=0;i<100;i++){
    NumPhoton[i]=0;
    NOR[i]=0;
  }
  TrackEdep = 0;
  TrackPL = 0;  

  for(int iPMT=0;iPMT<nPMT;iPMT++){
     vpmt[iPMT].clear();
  }
  vparticle.clear();
  vneutron.clear();
  vargon.clear();
  vgamma.clear();
  ngamma = 0;
  vekin.clear();

  G4cout<<"----- Begin of EventAction -----"<<G4endl;
}


//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void DMXEventAction::EndOfEventAction(const G4Event* evt) {

  /******* Fill Tree ********/
  DMLROutput *out=runAct->GetOutput();
  out->EvNum = evt->GetEventID();
  out->nPhoton   = NumPhoton[0];
  out->nJPMThits = NumPhoton[1];
  out->nMPMT1hits = NumPhoton[2];
  out->nMPMT2hits = NumPhoton[3];
  out->nMPMT3hits = NumPhoton[4];
  out->nMPMT4hits = NumPhoton[5];
  out->nElectron   = NumPhoton[6];

  //out->InitTrack.Edep = TrackEdep;
  //out->InitTrack.PathLength = TrackPL;

  out->JPMTa->Clear();
  out->MPMT1a->Clear();
  out->MPMT2a->Clear();
  out->MPMT3a->Clear();
  out->MPMT4a->Clear();
  out->Particlea->Clear();
  out->Neutrona->Clear();
  out->Argona->Clear();
  out->Gammaa->Clear();

  TClonesArray *JPMTA = out->JPMTa;
  TClonesArray *MPMT1A = out->MPMT1a;
  TClonesArray *MPMT2A = out->MPMT2a;
  TClonesArray *MPMT3A = out->MPMT3a;
  TClonesArray *MPMT4A = out->MPMT4a;
  TClonesArray *ParticleA = out->Particlea;
  TClonesArray *NeutronA = out->Neutrona;
  TClonesArray *ArgonA = out->Argona;
  TClonesArray *GammaA = out->Gammaa;

  PMT *jpmt=new PMT();
  PMT *mpmt1=new PMT();
  PMT *mpmt2=new PMT();
  PMT *mpmt3=new PMT();
  PMT *mpmt4=new PMT();
  Particle *particle1=new Particle();
  Particle *pNeutron=new Particle();
  Particle *pArgon=new Particle();
  Particle *pGamma=new Particle();

  int sizeparticle = vparticle.size();  
  for(int istep=0;istep<sizeparticle;istep++){
    new ((*ParticleA)[istep]) Particle();
    particle1=(Particle*)ParticleA->At(istep);
    particle1->SetObjects( vparticle[istep].particlename,
			   vparticle[istep].processname,
			   vparticle[istep].volumename,
			   vparticle[istep].TrackID,
			   vparticle[istep].ParentID,
			   vparticle[istep].xpre,
			   vparticle[istep].ypre,
			   vparticle[istep].zpre,
			   vparticle[istep].xpos,
			   vparticle[istep].ypos,
			   vparticle[istep].zpos,
			   vparticle[istep].edep,
			   vparticle[istep].time,
			   vparticle[istep].ekin
			   );
  }
  int sizeneutron = vneutron.size();
  for(int i=0;i<sizeneutron;i++){
    new ((*NeutronA)[i]) Particle();
    pNeutron=(Particle*)NeutronA->At(i);
    pNeutron->SetObjects( vneutron[i].particlename,
			  vneutron[i].processname,
			  vneutron[i].volumename,
			  vneutron[i].TrackID,
			  vneutron[i].ParentID,
			  vneutron[i].xpre,
			  vneutron[i].ypre,
			  vneutron[i].zpre,
			  vneutron[i].xpos,
			  vneutron[i].ypos,
			  vneutron[i].zpos,
			  vneutron[i].edep,
			  vneutron[i].time,
			  vneutron[i].ekin
			  );
  }
  int sizeargon = vargon.size();
  for(int i=0;i<sizeargon;i++){
    new ((*ArgonA)[i]) Particle();
    pArgon=(Particle*)ArgonA->At(i);
    pArgon->SetObjects( vargon[i].particlename,
			vargon[i].processname,
			vargon[i].volumename,
			vargon[i].TrackID,
			vargon[i].ParentID,
			vargon[i].xpre,
			vargon[i].ypre,
			vargon[i].zpos,
			vargon[i].xpos,
			vargon[i].ypos,
			vargon[i].zpos,
			vargon[i].edep,
			vargon[i].time,
			vargon[i].ekin
			);
  }
  int sizegamma = vgamma.size();
  for(int i=0;i<sizegamma;i++){
    new ((*GammaA)[i]) Particle();
    pGamma=(Particle*)GammaA->At(i);
    pGamma->SetObjects( vgamma[i].particlename,
			vgamma[i].processname,
			vgamma[i].volumename,
			vgamma[i].TrackID,
			vgamma[i].ParentID,
			vgamma[i].xpre,
			vgamma[i].ypre,
			vgamma[i].zpre,
			vgamma[i].xpos,
			vgamma[i].ypos,
			vgamma[i].zpos,
			vgamma[i].edep,
			vgamma[i].time,
			vgamma[i].ekin
			);
  }

  for(int iJhit=0;iJhit<NumPhoton[1];iJhit++){
    new ((*JPMTA)[iJhit]) PMT();
    jpmt=(PMT*)JPMTA->At(iJhit);    
    jpmt->SetObjects( vpmt[0][iJhit].xpos,
		      vpmt[0][iJhit].ypos,
		      vpmt[0][iJhit].zpos,
		      vpmt[0][iJhit].time,
		      vpmt[0][iJhit].ekin
		      );
  }
  for(int iMhit=0;iMhit<NumPhoton[2];iMhit++){
    new ((*MPMT1A)[iMhit]) PMT();
    mpmt1=(PMT*)MPMT1A->At(iMhit);    
    mpmt1->SetObjects( vpmt[1][iMhit].xpos,
		       vpmt[1][iMhit].ypos,
		       vpmt[1][iMhit].zpos,
		       vpmt[1][iMhit].time,
		       vpmt[1][iMhit].ekin
		       );
  }
  for(int iMhit=0;iMhit<NumPhoton[3];iMhit++){
    new ((*MPMT2A)[iMhit]) PMT();
    mpmt2=(PMT*)MPMT2A->At(iMhit);    
    mpmt2->SetObjects( vpmt[2][iMhit].xpos,
		       vpmt[2][iMhit].ypos,
		       vpmt[2][iMhit].zpos,
		       vpmt[2][iMhit].time,
		       vpmt[2][iMhit].ekin
		       );
  }
  for(int iMhit=0;iMhit<NumPhoton[4];iMhit++){
    new ((*MPMT3A)[iMhit]) PMT();
    mpmt3=(PMT*)MPMT3A->At(iMhit);    
    mpmt3->SetObjects( vpmt[3][iMhit].xpos,
		       vpmt[3][iMhit].ypos,
		       vpmt[3][iMhit].zpos,
		       vpmt[3][iMhit].time,
		       vpmt[3][iMhit].ekin
		       );
  }
  for(int iMhit=0;iMhit<NumPhoton[5];iMhit++){
    new ((*MPMT4A)[iMhit]) PMT();
    mpmt4=(PMT*)MPMT4A->At(iMhit);    
    mpmt4->SetObjects( vpmt[4][iMhit].xpos,
		       vpmt[4][iMhit].ypos,
		       vpmt[4][iMhit].zpos,
		       vpmt[4][iMhit].time,
		       vpmt[4][iMhit].ekin
		       );
  }


  out->Cd();
  out->Fill();

  G4cout << "All:" << NumPhoton[0] << " JPMT:" << NumPhoton[1] 
	 << " Meg1:" << NumPhoton[2] << " Meg2:" << NumPhoton[3] 
	 << " Meg3:" << NumPhoton[4] << " Meg4:" << NumPhoton[5] 
	 << " PMT-all:" << NumPhoton[1]+NumPhoton[2]+NumPhoton[3]+NumPhoton[4]+NumPhoton[5]
	 << " electron:" << NumPhoton[6] << G4endl;

  //  G4cout <<"Edep  = "<< G4BestUnit(TrackEdep,"Energy")<<G4endl;
  // G4cout <<"PathL = "<< G4BestUnit(TrackPL,"Length")<<G4endl;
  G4cout<<"----- End of EventAction -----"<<G4endl;
  G4cout<<G4endl;
  
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void DMXEventAction::writeScintHitsToFile(void) {

  //  G4String filename="hits.out";
  G4String filename=runAct->GetsavehitsFile();
  std::ofstream hitsfile(filename, std::ios::app);
  if(!event_id) {
    std::ofstream hitsfile(filename);
    hitsfile <<"Evt     Eprim   Etot    LXe     LXeTime PMT     PMTTime Seed1           Seed2           First   Flags" 
	     << G4endl;
    hitsfile <<"#       MeV     MeV     hits    ns      hits    ns                                      hit"
	     << G4endl
	     << G4endl;
  }

  if(S_hits) {

    if(hitsfile.is_open()) {


      hitsfile << std::setiosflags(std::ios::fixed)
	       << std::setprecision(4)
	       << std::setiosflags(std::ios::left)
	       << std::setw(6)
	       << event_id << "\t"
	       << energy_pri/MeV << "\t" 
	       << totEnergy/MeV << "\t"
	       << S_hits  << "\t"
	       << std::setiosflags(std::ios::scientific) 
	       << std::setprecision(2)
	       << firstLXeHitTime/nanosecond << "\t"
	       << P_hits << "\t"
	       << std::setiosflags(std::ios::fixed) 
	       << std::setprecision(4)
	       << aveTimePmtHits/nanosecond << "\t"
	       << *seeds     << "\t"
	       << *(seeds+1) << "\t"
	       << firstParticleName << "\t"
	       << (gamma_ev    ? "gamma " : "") 
	       << (neutron_ev  ? "neutron " : "") 
	       << (positron_ev ? "positron " : "") 
	       << (electron_ev ? "electron " : "") 
	       << (other_ev    ? "other " : "") 
	       << G4endl;

      if (event_id%printModulo == 0)
	G4cout << "     Event summary in file " << filename << G4endl;  
      hitsfile.close();
    }

#ifdef G4ANALYSIS_USE
    long seed1 = *seeds;
    long seed2 = *(seeds+1);    
    // pass event summary to analysis manager for booking into histos and ntple
    DMXAnalysisManager* analysis = DMXAnalysisManager::getInstance();
    analysis->analyseScintHits(event_id,energy_pri,totEnergy,S_hits,firstLXeHitTime,P_hits,aveTimePmtHits,firstParticleName,firstParticleE,gamma_ev,neutron_ev,positron_ev,electron_ev,other_ev,seed1,seed2);
#endif
  }

}


//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void DMXEventAction::writePmtHitsToFile(const DMXPmtHitsCollection* hits) {

  //  G4String filename="pmt.out";
  G4String filename=runAct->GetsavepmtFile();
  std::ofstream pmtfile(filename, std::ios::app);
  G4double x; G4double y; G4double z;

  if(pmtfile.is_open()) {
    pmtfile << "Hit#    X, mm   Y, mm   Z, mm" << G4endl;       
    pmtfile << std::setiosflags(std::ios::fixed)
	    << std::setprecision(3)
	    << std::setiosflags(std::ios::left)
	    << std::setw(6);
    for (G4int i=0; i<P_hits; i++)
      {
	x = ((*hits)[i]->GetPos()).x()/mm;
	y = ((*hits)[i]->GetPos()).y()/mm;
	z = ((*hits)[i]->GetPos()).z()/mm;
	pmtfile << i << "\t"
		<< x << "\t" 
		<< y << "\t"
		<< z << G4endl;
#ifdef G4ANALYSIS_USE
  	// pass pmt hit summary to analysis manager for booking in ntples
	DMXAnalysisManager* analysis = DMXAnalysisManager::getInstance();
	analysis->analysePMTHits(event_id,i,x,y,z);
#endif
      }
    if (event_id%printModulo == 0 && P_hits > 0) 
      G4cout << "     " << P_hits << " PMT hits in " << filename << G4endl;  
    pmtfile.close();
  }
  
}



//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
void DMXEventAction::drawTracks(const G4Event* evt) {

  if(G4VVisManager::GetConcreteInstance()) {
    G4UImanager::GetUIpointer()->ApplyCommand("/vis/scene/notifyHandlers");    
    G4TrajectoryContainer* trajContainer = evt->GetTrajectoryContainer();
    G4int n_trajectories = 0;

    if(trajContainer) n_trajectories = trajContainer->entries();
    for (G4int i=0; i<n_trajectories; i++) {
      G4Trajectory* trj = (G4Trajectory*)(*trajContainer)[i];
      if (drawTrksFlag == "all") 
	trj->DrawTrajectory();
      else if ((drawTrksFlag == "charged") && (trj->GetCharge() != 0.))
	trj->DrawTrajectory();
      else if ((drawTrksFlag == "noscint") 
	       && (trj->GetParticleName() != "opticalphoton"))
	trj->DrawTrajectory();
    }
    
    //    G4UImanager::GetUIpointer()->ApplyCommand("/vis/viewer/update");    
  } 

}
